Android内存分析和调优(上) 您所在的位置:网站首页 android 命令 Android内存分析和调优(上)

Android内存分析和调优(上)

2023-04-09 10:36| 来源: 网络整理| 查看: 265

PID    Vss        Rss        Pss       Uss      cmdline

......2319 42068K 42032K 13536K 7028K com.xxx......

该命令可以列出当前系统所有进程的内存占用情况。PID是进程ID。Vss是占用的虚拟内存,如果没有映射实际的内存也算进来。Rss是占用的物理内存。是共享内存+私有内存。因为共享内存是多个进程共用的,所以存在重复计算。Pss是占用的私有内存加上平分的共享内存。例如一块1M的共享内存被两个进程共享,那每个进程分500K。各进程的Pss相加基本等于实际被使用的物理内存,所以这个经常是最重要的参数。Uss是私有内存。cmdline可以看做是apk包名。

通过procrank,只能很宏观的横向比较不同的应用。如果要更细致的了解具体内存是如何使用,则需要进入

第二层 dumpsys meminfo

命令“adb shell dumpsys meminfo package.name”。在4.0 ICS(或者3.0 HoneyComb)之后的系统上,会看到类似下面的输出

                                 Shared   Private  Heap    Heap     Heap                      Pss      Dirty      Dirty     Size     Alloc      Free                      ------   ------    ------     ------  ------     ------Native            16        8           16        3416   3300     79Dalvik            3884    10592   3580    9560   9022     538Cursor            0          0           0 Ashmem        0           0           0 Other dev       5110    10244   0 .so mmap       640     1948      396 .jar mmap      0          0           0 .apk mmap     68        0           0 .ttf mmap       817      0           0 .dex mmap     411      0           0 Other mmap   55        16         32 Unknown        2404     660       2388 TOTAL            13405  23468   6412    12976 12322 617

(如果使用2.3或之前的版本,结果会粗糙一些,很多都被归入了Other,但基本结构是一样的)

stacktrace上有个经常被搜到的帖子对这个格式有说明,虽然针对的是android 2.3格式,但读后非常有收获。但仍有很多疑问没有解答,例如针对上面的例子,为什么Native heap size那么大,但Pss却那么小?占用内存比较多的Other dev是什么?Unknown又有哪些?等等。要理解这些,需要知道这个report是如何生成的。实际上,生成report的代码是android的android_os_Debug.cpp。从中我们可以发现,上面列表的数据是由三种方式获取的:1. Pss/Shared Dirty/Private Dirty三列是读取了/proc/process-id/smaps文件获取的。它会对每个虚拟内存块进行解析,然后生成数据。2. Native Heap Size/Alloc/Free三列是使用C函数mallinfo得到的。3. Dalvik Heap Size/Alloc/Free并非该cpp文件产生,而是android的Debug类生成。

后面两个Heap的获取比较简单,我唯一的疑惑是为什么有free的?我的理解是无论是c的malloc还是java的new,最后都是通过mmap系统调用进行内存分配的。而mmap必须以页的4K为单位。所以如果一次一次只需要malloc 2K,则剩下的2K是free的。如果下次再malloc 2K,可以仍然使用上次mmap剩余的2K内存。

至于smaps文件,我们可以通过adb shell cat /proc/process-id/smaps来查看(需要root)。这是个普通的linux文件,描述了进程的虚拟内存区域(vm area)的具体信息。每次mmap一般都会生成一个vm area。在Android上,一个更加方便的命令是adb shell showmap -a process-id。

第三层 adb shell showmap

该命令也是读取smaps文件,但结果细化的具体的vm area。该命令输出的每行表示一个vm area,列出了该vm area的start addr, end addr, Vss, Rss, Pss, shared clean, shared dirty, private clean, private dirty,object。 第二层的dumpsys meminfo其实就是读取这些数据,然后分类(native, dalvik, .so map, etc.)统计生成。start addr和end addr表示进程空间的起止虚拟地址。Vss,Rss,Pss跟前面说的一样。Object可以看做mmap的文件名。

Shared clean,按字面意思,表示共享的干净的数据。共享表示多个进程的虚拟地址可以都指向这块物理空间,表示多个进程共享的so库。为什么这里说是多个进程共享的so而不是所有的so呢?关于so库的加载,我一直觉得是mmap带MAP_SHARED参数,但看了memory_faq,才知道是MAP_PRIVATE。如果使用showmap命令查看vm area,会发现有的so的内存都属于Shared clean,而有的so则属于private clean。前者一般是当前进程特有的so,而后者一般是通用的so。后来看了对mmap的各种参数的实验(很赞实践精神),才知道第一次以MAP_PRIVATE mmap so,内存都是private clean的。如果另外一个进程mmap了同一个so,那该vm area就变成shared clean了。

Private clean,包括该进程私有的干净的内存。包括前面说的该进程独自使用的so和进程的二进制代码段。Clean内存的好处是在内存紧张时,可以释放物理内存。因为是clean的,所以不需要写回到disk,只需要下次读取该内存(导致缺页错误)时再从disk读入。

Private dirty,表示该进程私有的不跟disk数据一致的内存段。例如堆(heap),栈(stack),bss段。关于bss段,因为在elf文件为了节约控件没有赋值,所以在加载到内存时赋值为0,于是跟disk就不一致了。在showmap结果中,会发现几乎每个so都有一个显示位[bss]的private dirty段。数据段我估计是private clean的,因为elf文件是有初值的。

Shared dirty开始我一直搞不清楚。后来看了Dalvik vm internal这个video(slides),才明白了些。对于普通的linux进程,当父进程fork子进程时,父进程的虚拟内存区域都会”复制“一份到子进程中。这里”复制“加引号,是因为为了节省内存,也为了减少内存拷贝的时间,使用的是copy-on-write的方法。当子进程对private dirty的堆,栈,bss没有修改时,则是父子进程share这份dirty(因为跟disk没法映射)数据。如果发生改变,则会修改为private dirty。所以android有zygote进程,是所有android apps进程的父进程,在其中会加载resource等资源(下文会看到,最简单的应该也有大概5M resource,例如图片),这些资源都是只读的。具体的apps继承了这些shared dirty的数据,因为不修改它们,所以也不用分配多余的内存空间。

由于android使用的linux没有swap分区,所以dirty的数据必须常驻内存。所以dumpsys meminfo会把private dirty和shared dirty重点列出来,这也是我们优化内存的重点。

现在可以回答一个前面提到的问题,为什么Native Heap(根据mallinfo系统调用得到)很大而Native Pss(根据swaps得到)很小。我觉得这是dumpsys meminfo的一个bug。根据android_os_Debug.cpp的代码,object名字是[heap]的段被认为是native heap。这在2.3是正确的,但在4.0之后,[heap]为名字的段却很小(只有几K)。同时,我却发现有大量的[anon]的区域。我认为anon是anonymous的缩写。malloc一般是通过mmap来分配内存的,而参数是MAP_ANONYMOUS。所以我觉得这些[anon]是native heap。从大小上看,现在这些[anon]被看做是Unkown的一部分,也跟hative heap的大小差不多。

在dumpsys meminfo结果的其他值比较大的行,.so表示映射的so库(vm area行的object名称包含.so字样),.dex表示映射的.dex文件(dalvik的虚拟机二进制码),Other dev表示映射其他的/dev的(dalvik的heap也是映射到特殊的/dev上)。加上native和dalvik的heap,下次写如何具体分析这五项。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有